﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows;
using MaxStoreErp.Core.Annotations;
using MaxStoreErp.Core.Attributes;
using MaxStoreErp.Core.BaseClasses;
using MaxStoreErp.Core.Commands;
using MaxStoreErp.Core.Interfaces;
using MaxStoreErp.Core.Threading;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Prism.UnityExtensions;
using Microsoft.Practices.Unity;

namespace MaxStoreErp.Core.Infrastructure
{
    /// <summary>
    /// Навигатор региона
    /// </summary>
    public class DocumentManager<TMainMenu> : IDocumentManager, INotifyPropertyChanged
        where TMainMenu : IDocument, new()
    {
        const int MaxStackSize = 5;
        readonly IUnityContainer _container;
        readonly IRegionManager _regionManager;
        readonly FixedStack<IDocument> _documents;
        ReadOnlyCollection<FrameworkElement> _lastToolBarItems;
        ReadOnlyCollection<FrameworkElement> _lastStatusBarItems;

        public DocumentManager(IUnityContainer container)
        {
            _container = container;
            _regionManager = container.TryResolve<IRegionManager>();
            _documents = new FixedStack<IDocument>(MaxStackSize);
            container.Resolve<ShellGlobalCommands>().MainMenu = new RelayCommand(NavigateToMenu);
            container.Resolve<ShellGlobalCommands>().Login = new RelayCommand(NavigateToLogin);
            container.Resolve<ShellGlobalCommands>().Back = new RelayCommand(Back, () => _documents.Count > 0);
        }

        public void NavigateTo<T>(Action onNavigation=null) where T : IDocument, new()
        {
            ThreadHelper.SafeCall(() =>
            {
                IDocument view = _container.TryResolve<T>();
                if (view == null)
                    return;

                NavigateTo(view.GetDocumentView(true));
            });
        }

        public void NavigateTo(IView view, Action onNavigation = null)
        {
            var args = new CancelEventArgs();
            RemovePreviousView(args);
            if (args.Cancel)
                return;

            ActivateView(view);
        }

        void RemovePreviousView(CancelEventArgs eventArgs, bool saveInHistory = true)
        {
            var targetRegion = _regionManager.Regions[PrismRegionNames.MainWindowRegion];

            var removingView = targetRegion.ActiveViews.FirstOrDefault() as IView;
            if (removingView == null)
                return;

            removingView.Presenter.OnViewClosing(eventArgs);
            if (eventArgs.Cancel)
                return;

            removingView.Presenter.OnViewClosed();
            RemoveToolBarItems();
            RemoveStatusBarItems();
            if (removingView.Presenter.GetType().GetCustomAttribute<HistoryNotRememberedAttribute>(false) == null && saveInHistory)
                _documents.Push(removingView.Presenter);

            targetRegion.Remove(removingView);
        }

        void ActivateView(IView view)
        {
            var targetRegion = _regionManager.Regions[PrismRegionNames.MainWindowRegion];
            view.Presenter.OnViewLoading();
            AddToolBarItems(view);
            AddStatusBarItems(view);
            targetRegion.Add(view);
            targetRegion.Activate(view);
        }

        void AddToolBarItems(IView view)
        {
            var toolBarProvider = view.Presenter as IToolBarProvider;
            if (toolBarProvider == null) 
                return;

            _lastToolBarItems = toolBarProvider.ToolBarItems;
            var shell = _container.Resolve<IShellWindow>();
            shell.PlaceToolBarItems(toolBarProvider.ToolBarItems);
        }

        void RemoveToolBarItems()
        {
            if (_lastToolBarItems == null)
                return;

            var shell = _container.Resolve<IShellWindow>();
            shell.RemoveToolBarItems(_lastToolBarItems);
            _lastToolBarItems = null;
        }

        void AddStatusBarItems(IView view)
        {
            var statusBarProvider = view.Presenter as IStatusBarProvider;
            if (statusBarProvider == null)
                return;

            _lastStatusBarItems = statusBarProvider.StatusBarItems;
            var shell = _container.Resolve<IShellWindow>();
            shell.PlaceStatusBarItems(statusBarProvider.StatusBarItems);
        }

        void RemoveStatusBarItems()
        {
            if (_lastStatusBarItems == null)
                return;

            var shell = _container.Resolve<IShellWindow>();
            shell.RemoveStatusBarItems(_lastStatusBarItems);
            _lastStatusBarItems = null;
        }

        public void Back()
        {
            if (_documents.Count <= 0) 
                return;

            var args = new CancelEventArgs();
            RemovePreviousView(args, false);
            if (args.Cancel)
                return;

            ActivateView(_documents.Pop().GetDocumentView(true));
        }

        public void NavigateToMenu()
        {
            _documents.Clear();
            NavigateTo<TMainMenu>();
        }

        public void NavigateToLogin()
        {
            var userLogin = _container.TryResolve<IUserLogin>();
            if (userLogin != null)
            {
                NavigateTo(userLogin.GetLoginView(this), OnLoginLoaded);
            }
        }

        void OnLoginLoaded()
        {
            _container.Resolve<IShellWindow>().LockToolBar();
        }

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }
}
